package gek.uni.aspirant.ocl.parser.ocl.visitors;

import gek.uni.aspirant.ocl.parser.mptree.tree.utils.OfflineUtils;
import gek.uni.aspirant.ocl.parser.mptree.tree.utils.PrologConstants;
import gek.uni.aspirant.ocl.parser.mptree.tree.utils.VariableNameManager;
import gek.uni.aspirant.ocl.types.prolog.CodeGeneratorPair;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.eclipse.ocl.expressions.BooleanLiteralExp;
import org.eclipse.ocl.expressions.IntegerLiteralExp;
import org.eclipse.ocl.expressions.NullLiteralExp;
import org.eclipse.ocl.expressions.OCLExpression;
import org.eclipse.ocl.expressions.OperationCallExp;
import org.eclipse.ocl.expressions.PropertyCallExp;
import org.eclipse.ocl.expressions.RealLiteralExp;
import org.eclipse.ocl.expressions.StringLiteralExp;
import org.eclipse.ocl.expressions.TypeExp;
import org.eclipse.ocl.expressions.VariableExp;
import org.eclipse.ocl.expressions.IteratorExp;
import org.eclipse.ocl.utilities.AbstractVisitor;
import org.eclipse.ocl.utilities.PredefinedType;
import org.eclipse.uml2.uml.CallOperationAction;
import org.eclipse.uml2.uml.Classifier;
import org.eclipse.uml2.uml.Constraint;
import org.eclipse.uml2.uml.EnumerationLiteral;
import org.eclipse.uml2.uml.Operation;
import org.eclipse.uml2.uml.Parameter;
import org.eclipse.uml2.uml.Property;
import org.eclipse.uml2.uml.SendSignalAction;
import org.eclipse.uml2.uml.State;

public class PrologCodeGeneratorVisitor
    extends
    AbstractVisitor<CodeGeneratorPair, Classifier, Operation, Property, EnumerationLiteral, Parameter, State, CallOperationAction, SendSignalAction, Constraint> {

  private Map<String, OCLExpression> stringToEssensionMap = new HashMap<String, OCLExpression>();

  public Map<String, OCLExpression> getStringToEssensionMap() {
    return stringToEssensionMap;
  }

  public void setStringToEssensionMap(
      Map<String, OCLExpression> stringToEssensionMap) {
    this.stringToEssensionMap = stringToEssensionMap;
  }

  @Override
  public CodeGeneratorPair visitVariableExp(VariableExp<Classifier, Parameter> v) {
    String oclVariableName = v.getName();
    
    String prologVariableName = OfflineUtils
        .createPrologVariableName(oclVariableName);

    CodeGeneratorPair result = new CodeGeneratorPair(null, prologVariableName);
    stringToEssensionMap.put(prologVariableName, v);
    return result;
  }
  
  /**
   * В этом методе обрабатывается объект класса Type. Можно вытащить имя класса
   */
  @Override
  public CodeGeneratorPair visitTypeExp(TypeExp<Classifier> t) {
	String referredType = t.getReferredType().getName().toLowerCase(); 
	if (!referredType.isEmpty())
		stringToEssensionMap.put(referredType, t);
    return new CodeGeneratorPair(null, referredType);
    //return super.visitTypeExp(t);
  }

  @Override
  protected CodeGeneratorPair handlePropertyCallExp(
      PropertyCallExp<Classifier, Property> callExp,
      CodeGeneratorPair sourceResult, List<CodeGeneratorPair> qualifierResults) {

    String sourceName = OfflineUtils.createPrologVariableName(callExp
        .getSource().getName());

    String originalPropName = callExp.getReferredProperty().getName();

    String prologPropertyName = OfflineUtils
        .createPrologVariableName(originalPropName);
    String propertyName = sourceName + "_" + prologPropertyName;

    stringToEssensionMap.put(propertyName, callExp);
    CodeGeneratorPair result = new CodeGeneratorPair(null, propertyName);
    return result;
  }

	@Override
	public CodeGeneratorPair visitIntegerLiteralExp(
			IntegerLiteralExp<Classifier> literalExp) {
		return new CodeGeneratorPair(null, String.valueOf(literalExp
				.getIntegerSymbol()));
	}

	public CodeGeneratorPair visitRealLiteralExp(
			RealLiteralExp<Classifier> literalExp) {
		return new CodeGeneratorPair(null, String.valueOf(literalExp
				.getRealSymbol()));
	}

	public CodeGeneratorPair visitNullLiteralExp(
			NullLiteralExp<Classifier> literalExp) {
		return new CodeGeneratorPair(null, String.valueOf("null"));
	}

	@Override
	public CodeGeneratorPair visitBooleanLiteralExp(
			BooleanLiteralExp<Classifier> literalExp) {
		return new CodeGeneratorPair(null, String.valueOf(literalExp
				.getBooleanSymbol()));
	}

  @Override
	public CodeGeneratorPair visitStringLiteralExp(
			StringLiteralExp<Classifier> literalExp) {
		// TODO Auto-generated method stub
		return super.visitStringLiteralExp(literalExp);
	}

  // TODO
  @Override
  protected CodeGeneratorPair handleOperationCallExp(
      OperationCallExp<Classifier, Operation> callExp,
      CodeGeneratorPair sourceResult, List<CodeGeneratorPair> argumentResults) {

    int operationCode = callExp.getOperationCode();
    switch (operationCode) {
    case PredefinedType.OCL_IS_TYPE_OF:
      return doHandleIsTypeOfOperation(callExp, sourceResult, argumentResults);
    case PredefinedType.OCL_IS_KIND_OF:
      return doHandleIsKindOfOperation(callExp, sourceResult, argumentResults);
    case PredefinedType.PLUS:
        return doHandlePlusOperation(callExp, sourceResult, argumentResults);
    case PredefinedType.MINUS:
        return doHandleMinusOperation(callExp, sourceResult, argumentResults);
    case PredefinedType.TIMES:
        return doHandleMultiplicationOperation(callExp, sourceResult, argumentResults);
    case PredefinedType.DIVIDE:
        return doHandleDivisionOperation(callExp, sourceResult, argumentResults);        
    case PredefinedType.EQUAL:
      return doHandleEqualOperation(callExp, sourceResult, argumentResults);
    case PredefinedType.LESS_THAN:
      return doHandleLessThanOperation(callExp, sourceResult, argumentResults);
    case PredefinedType.LESS_THAN_EQUAL:
        return doHandleLessThanEqualOperation(callExp, sourceResult, argumentResults);      
    case PredefinedType.GREATER_THAN:
      return doHandleGreaterThanOperation(callExp, sourceResult, argumentResults);
    case PredefinedType.GREATER_THAN_EQUAL:
        return doHandleGreaterThanEqualOperation(callExp, sourceResult, argumentResults);
    case PredefinedType.AND:
      return doHandleAndOperation(callExp, sourceResult, argumentResults);
    case PredefinedType.OR:
      return doHandleOrOperation(callExp, sourceResult, argumentResults);
    case PredefinedType.SIZE:
      return doHandleSizeOperation(callExp, sourceResult, argumentResults);
    case PredefinedType.IS_EMPTY:
      return doHandleIsEmptyOperation(callExp, sourceResult, argumentResults);
    case PredefinedType.OCL_IS_UNDEFINED:
      return doHandleOclIsUndefinedOperation(callExp, sourceResult, argumentResults);
    case PredefinedType.INCLUDES:
      return doHandleIncludesOperation(callExp, sourceResult, argumentResults);
    default:
      break;
    }
    return super.handleOperationCallExp(callExp, sourceResult, argumentResults);
  }
  
  // TODO
  @Override
  protected CodeGeneratorPair handleIteratorExp(
		  IteratorExp<Classifier, Parameter> callExp,
		  CodeGeneratorPair sourceResult, List<CodeGeneratorPair> variableResults, 
		  CodeGeneratorPair bodyResult) {
	
	String operationName = callExp.getName();
    if (operationName.equalsIgnoreCase(PredefinedType.SELECT_NAME)) {
      return doHandleSelectOperation(callExp, sourceResult, variableResults, bodyResult);
    }
    if (operationName.equalsIgnoreCase(PredefinedType.FOR_ALL_NAME)) {
      return doHandleForAllOperation(callExp, sourceResult, variableResults, bodyResult);
    }
    return super.handleIteratorExp(callExp, sourceResult, variableResults, bodyResult);
  }

  /**********************************************************************
   * OPERATIONS - METHODS
   *********************************************************************/

  /**
   * Трансляция OCL-операции "select(object)" в код на прологе.
   * {Collection}.select(<Expr>) --> 
   * --> <ResExpr>=true, select(<Expr>,<{Collection}>, <ResColl>),
   * <ResExpr> - это результат <expr>
   * Пример: oclIsUndefined(<VarName>, <ResExpr>)
   * 
   * @param callExp
   * @param sourceResult
   * @param variableResults
   * @param bodyResult
   * @return
   */
  private CodeGeneratorPair doHandleSelectOperation(
	IteratorExp<Classifier, Parameter> callExp, CodeGeneratorPair sourceResult, 
	List<CodeGeneratorPair> variableResults, CodeGeneratorPair bodyResult) {
	  
	// переменная, хранящая результат
	CodeGeneratorPair result = new CodeGeneratorPair();

	// билдер, который будет накапливать код
	StringBuilder bldr = new StringBuilder();

	// формируем код операции на прологе 
	// "<ResExpr>=true, select(<Expr>,<{Collection}>, <ResColl>),".
	String exprCodeName = bodyResult.getCode();
	String resExprVarName = bodyResult.getResultVarName();
	String sourceCollVarName = sourceResult.getResultVarName();
	String resultCollVarName = VariableNameManager.getInstance()
	    .getUniqueVarNameForOperation("c__Select");

	bldr.append(resExprVarName).append("=true, ")
	    .append(PrologConstants.PROLOG_SELECT_OPERATION_NAME).append("(")
	    .append(exprCodeName).append(sourceCollVarName).append(",")
	    .append(resultCollVarName).append(")").append(", ");

	// Задаем значения полей результата
	result.setCode(bldr.toString());
	result.setResultVarName(resultCollVarName);
	return result;
  }  
  
  /**
   * Трансляция OCL-операции "forAll(expr)" в код на прологе.
   * {Collection}.forAll(<Expr>) --> 
   * --> <ResExpr>=true, forall(<Expr>,<{Collection}>, <ResultBool>),
   * <ResExpr> - это результат <expr>
   * Пример: oclIsUndefined(<VarName>, <ResExpr>)
   * 
   * @param callExp
   * @param sourceResult
   * @param variableResults
   * @param bodyResult
   * @return
   */
  private CodeGeneratorPair doHandleForAllOperation(
	IteratorExp<Classifier, Parameter> callExp, CodeGeneratorPair sourceResult, 
	List<CodeGeneratorPair> variableResults, CodeGeneratorPair bodyResult) {
	  
	// переменная, хранящая результат
	CodeGeneratorPair result = new CodeGeneratorPair();

	// билдер, который будет накапливать код
	StringBuilder bldr = new StringBuilder();

	// формируем код операции на прологе 
	// "<ResExpr>=true, forall(<Expr>,<{Collection}>, <ResultBool>),".
	String exprCodeName = bodyResult.getCode();
	String resExprVarName = bodyResult.getResultVarName();
	String sourceCollVarName = sourceResult.getResultVarName();
	String resultBoolVarName = VariableNameManager.getInstance()
	    .getUniqueVarNameForOperation("b__ForAll");

	bldr.append(resExprVarName).append("=true, ")
	    .append(PrologConstants.PROLOG_FORALL_OPERATION_NAME).append("(")
	    .append(exprCodeName).append(sourceCollVarName).append(",")
	    .append(resultBoolVarName).append(")").append(", ");

	// Задаем значения полей результата
	result.setCode(bldr.toString());
	result.setResultVarName(resultBoolVarName);
	return result;
  }
  
  /**
   * Трансляция OCL-операции " isTypeOf() " в код на прологе.
   * <VarName>.isTypeOf(<Object>) --> 
   * --> oclIsTypeOf(<VarName>,<Object>,<ResultBool>),
   * 
   * @param callExp
   * @param sourceResult
   * @param argumentResults
   * @return
   */
  private CodeGeneratorPair doHandleIsTypeOfOperation(
      OperationCallExp<Classifier, Operation> callExp,
      CodeGeneratorPair sourceResult, List<CodeGeneratorPair> argumentResults) {

    // переменная, хранящая результат
    CodeGeneratorPair result = new CodeGeneratorPair();

    // билдер, который будет накапливать код
    StringBuilder bldr = new StringBuilder();

    // формируем код операции на прологе 
    // "oclIsTypeOf(<VarName>,<Object>,<ResultBool>),".
    String varName = sourceResult.getResultVarName();
    String objectVarName="";
    if (argumentResults != null)
    	objectVarName = argumentResults.get(0).getResultVarName();
    String resultVarName = VariableNameManager.getInstance()
        .getUniqueVarNameForOperation("b__OclIsTypeOf");

    bldr.append(PrologConstants.PROLOG_OCL_IS_TYPE_OF_OPERATION_NAME).append("(")
        .append(varName).append(",").append(objectVarName).append(",")
        .append(resultVarName).append(")").append(", ");

    // Задаем значения полей результата
    result.setCode(bldr.toString());
    result.setResultVarName(resultVarName); 
    return result;    
  }
  
  /**
   * Трансляция OCL-операции " isKindOf() " в код на прологе.
   * <VarName>.isKindOf(<Object>) --> 
   * --> oclIsKindOf(<VarName>,<Object>,<ResultBool>),
   * 
   * @param callExp
   * @param sourceResult
   * @param argumentResults
   * @return
   */
  private CodeGeneratorPair doHandleIsKindOfOperation(
      OperationCallExp<Classifier, Operation> callExp,
      CodeGeneratorPair sourceResult, List<CodeGeneratorPair> argumentResults) {

    // переменная, хранящая результат
    CodeGeneratorPair result = new CodeGeneratorPair();

    // билдер, который будет накапливать код
    StringBuilder bldr = new StringBuilder();

    // формируем код операции на прологе 
    // "oclIsKindOf(<VarName>,<Object>,<ResultBool>),".
    String varName = sourceResult.getResultVarName();
    String objectVarName="";
    if (argumentResults != null)
    	objectVarName = argumentResults.get(0).getResultVarName();
    String resultVarName = VariableNameManager.getInstance()
        .getUniqueVarNameForOperation("b__OclIsKindOf");

    bldr.append(PrologConstants.PROLOG_OCL_IS_KIND_OF_OPERATION_NAME).append("(")
        .append(varName).append(",").append(objectVarName).append(",")
        .append(resultVarName).append(")").append(", ");

    // Задаем значения полей результата
    result.setCode(bldr.toString());
    result.setResultVarName(resultVarName); 
    return result;    
  }
  
  
  /**
   * Трансляция OCL-операции " + (plus) " в код на прологе.
   * <LeftVar> + <RightVar> --> 
   * --> +(<LeftVar>,<RightVar>,<ResultInt>),
   * 
   * @param callExp
   * @param sourceResult
   * @param argumentResults
   * @return
   */
  private CodeGeneratorPair doHandlePlusOperation(
      OperationCallExp<Classifier, Operation> callExp,
      CodeGeneratorPair sourceResult, List<CodeGeneratorPair> argumentResults) {
	  
	// переменная, хранящая результат
	CodeGeneratorPair result = new CodeGeneratorPair();

	// билдер, который будет накапливать код
	StringBuilder bldr = new StringBuilder();

	// формируем код операции на прологе 
	// "+(<LeftVar>,<RightVar>,<ResultBool>),".
	String leftVarName = sourceResult.getResultVarName();
	String rightVarName = argumentResults.get(0).getResultVarName();
	String resultVarName = VariableNameManager.getInstance()
	    .getUniqueVarNameForOperation("i__Plus");
	
	bldr.append(PrologConstants.PROLOG_PLUS_OPERATION_NAME).append("(")
	    .append(leftVarName).append(",").append(rightVarName).append(",")
	    .append(resultVarName).append(")").append(", ");

	// если имеется код, пришедший от левой чайсти операции, то добавим его в
	// результат
	if (sourceResult.getCode() != null)
	  bldr.append(sourceResult.getCode());

	// для каждого из аргументов в правой части (а таковой должен быть ровно 1)
	// добавим имеющийся у него код в результат
	for (CodeGeneratorPair argResult : argumentResults) {
	  if (argResult.getCode() != null)
	    bldr.append(argResult.getCode());
	}	

	// Задаем значения полей результата
	result.setCode(bldr.toString());
	result.setResultVarName(resultVarName);
	return result;
  }
  
  /**
   * Трансляция OCL-операции " - (minus) " в код на прологе.
   * <LeftVar> - <RightVar> --> 
   * --> -(<LeftVar>,<RightVar>,<ResultInt>),
   * 
   * @param callExp
   * @param sourceResult
   * @param argumentResults
   * @return
   */
  private CodeGeneratorPair doHandleMinusOperation(
      OperationCallExp<Classifier, Operation> callExp,
      CodeGeneratorPair sourceResult, List<CodeGeneratorPair> argumentResults) {
	  
	// переменная, хранящая результат
	CodeGeneratorPair result = new CodeGeneratorPair();

	// билдер, который будет накапливать код
	StringBuilder bldr = new StringBuilder();

	// формируем код операции на прологе 
	// "-(<LeftVar>,<RightVar>,<ResultBool>),".
	String leftVarName = sourceResult.getResultVarName();
	String rightVarName = argumentResults.get(0).getResultVarName();
	String resultVarName = VariableNameManager.getInstance()
	    .getUniqueVarNameForOperation("i__Minus");
	
	bldr.append(PrologConstants.PROLOG_MINUS_OPERATION_NAME).append("(")
	    .append(leftVarName).append(",").append(rightVarName).append(",")
	    .append(resultVarName).append(")").append(", ");

	// если имеется код, пришедший от левой чайсти операции, то добавим его в
	// результат
	if (sourceResult.getCode() != null)
	  bldr.append(sourceResult.getCode());

	// для каждого из аргументов в правой части (а таковой должен быть ровно 1)
	// добавим имеющийся у него код в результат
	for (CodeGeneratorPair argResult : argumentResults) {
	  if (argResult.getCode() != null)
	    bldr.append(argResult.getCode());
	}	

	// Задаем значения полей результата
	result.setCode(bldr.toString());
	result.setResultVarName(resultVarName);
	return result;
  }
  
  /**
   * Трансляция OCL-операции " * (times) " в код на прологе.
   * <LeftVar> * <RightVar> --> 
   * --> *(<LeftVar>,<RightVar>,<ResultInt>),
   * 
   * @param callExp
   * @param sourceResult
   * @param argumentResults
   * @return
   */
  private CodeGeneratorPair doHandleMultiplicationOperation(
      OperationCallExp<Classifier, Operation> callExp,
      CodeGeneratorPair sourceResult, List<CodeGeneratorPair> argumentResults) {
	  
	// переменная, хранящая результат
	CodeGeneratorPair result = new CodeGeneratorPair();

	// билдер, который будет накапливать код
	StringBuilder bldr = new StringBuilder();

	// формируем код операции на прологе 
	// "*(<LeftVar>,<RightVar>,<ResultInt>),".
	String leftVarName = sourceResult.getResultVarName();
	String rightVarName = argumentResults.get(0).getResultVarName();
	String resultVarName = VariableNameManager.getInstance()
	    .getUniqueVarNameForOperation("i__Multi");
	
	bldr.append(PrologConstants.PROLOG_MULTI_OPERATION_NAME).append("(")
	    .append(leftVarName).append(",").append(rightVarName).append(",")
	    .append(resultVarName).append(")").append(", ");

	// если имеется код, пришедший от левой чайсти операции, то добавим его в
	// результат
	if (sourceResult.getCode() != null)
	  bldr.append(sourceResult.getCode());

	// для каждого из аргументов в правой части (а таковой должен быть ровно 1)
	// добавим имеющийся у него код в результат
	for (CodeGeneratorPair argResult : argumentResults) {
	  if (argResult.getCode() != null)
	    bldr.append(argResult.getCode());
	}	

	// Задаем значения полей результата
	result.setCode(bldr.toString());
	result.setResultVarName(resultVarName);
	return result;
  }

  /**
   * Трансляция OCL-операции " / (divide) " в код на прологе.
   * <LeftVar> / <RightVar> --> 
   * --> /(<LeftVar>,<RightVar>,<ResultInt>),
   * 
   * @param callExp
   * @param sourceResult
   * @param argumentResults
   * @return
   */
  private CodeGeneratorPair doHandleDivisionOperation(
      OperationCallExp<Classifier, Operation> callExp,
      CodeGeneratorPair sourceResult, List<CodeGeneratorPair> argumentResults) {
	  
	// переменная, хранящая результат
	CodeGeneratorPair result = new CodeGeneratorPair();

	// билдер, который будет накапливать код
	StringBuilder bldr = new StringBuilder();

	// формируем код операции на прологе 
	// "/(<LeftVar>,<RightVar>,<ResultInt>),".
	String leftVarName = sourceResult.getResultVarName();
	String rightVarName = argumentResults.get(0).getResultVarName();
	String resultVarName = VariableNameManager.getInstance()
	    .getUniqueVarNameForOperation("i__Divide");
	
	bldr.append(PrologConstants.PROLOG_DIVIDE_OPERATION_NAME).append("(")
	    .append(leftVarName).append(",").append(rightVarName).append(",")
	    .append(resultVarName).append(")").append(", ");

	// если имеется код, пришедший от левой чайсти операции, то добавим его в
	// результат
	if (sourceResult.getCode() != null)
	  bldr.append(sourceResult.getCode());

	// для каждого из аргументов в правой части (а таковой должен быть ровно 1)
	// добавим имеющийся у него код в результат
	for (CodeGeneratorPair argResult : argumentResults) {
	  if (argResult.getCode() != null)
	    bldr.append(argResult.getCode());
	}	

	// Задаем значения полей результата
	result.setCode(bldr.toString());
	result.setResultVarName(resultVarName);
	return result;
  } 
  
  /**
   * Трансляция OCL-операции " = " в код на прологе.
   * <LeftVar>=<RightVar> --> 
   * --> =(<LeftVar>,<RightVar>,<ResultBool>),
   * 
   * @param callExp
   * @param sourceResult
   * @param argumentResults
   * @return
   */
  private CodeGeneratorPair doHandleEqualOperation(
      OperationCallExp<Classifier, Operation> callExp,
      CodeGeneratorPair sourceResult, List<CodeGeneratorPair> argumentResults) {

    // переменная, хранящая результат
    CodeGeneratorPair result = new CodeGeneratorPair();

    // билдер, который будет накапливать код
    StringBuilder bldr = new StringBuilder();

    // формируем код операции на прологе 
    // "=(<LeftVar>,<RightVar>,<ResultBool>),".
    String leftVarName = sourceResult.getResultVarName();
    String rightVarName = argumentResults.get(0).getResultVarName();
    
    // создаем необходимые для опеределения типов переменные
    String leftOperationType="";
    String rightOperationType="";
    String bothOperationType="A";
    
    boolean leftOperandIsOperationOrVar = false;
    boolean rightOperandIsOperationOrVar = false;
    
    // проверяем, можем ли мы узнать тип левого операнда
    if (stringToEssensionMap.containsKey(leftVarName)) {
    	// Значит это может быть переменная
    	leftOperandIsOperationOrVar = true;
    	String leftVarNameType = 
    		((OCLExpression<Classifier>)stringToEssensionMap.
    				get(leftVarName)).getType().getName();
    	leftOperationType = leftVarNameType.substring(0,1);
    	if (leftVarNameType.startsWith("Set"))
    		leftOperationType="C";
    } else {
    	// Или это может быть результат операции
    	if (operandIsOperationResult(leftVarName)) {
    		leftOperandIsOperationOrVar = true;
    		leftOperationType = leftVarName.substring(0,1);
    	}
    }
    
    // проверяем, можем ли мы узнать тип правого операнда
    if (stringToEssensionMap.containsKey(rightVarName)) {
    	// Значит это может быть переменная
    	rightOperandIsOperationOrVar = true;
    	String rightVarNameType = 
    		((OCLExpression<Classifier>)stringToEssensionMap.
    				get(leftVarName)).getType().getName();
    	rightOperationType = rightVarNameType.substring(0,1);
    	if (rightVarNameType.startsWith("Set"))
    		rightOperationType="C";
    } else {
    	// Или это может быть результат операции
    	if (operandIsOperationResult(rightVarName)) {
    		rightOperandIsOperationOrVar = true;
    		rightOperationType = rightVarName.substring(0,1);
    	}
    }
    
    // если оба операнда - либо результат операции, либо переменная, то
    // определяем их тип
    if ((leftOperandIsOperationOrVar)&&(rightOperandIsOperationOrVar)) {
    	if (leftOperationType.equalsIgnoreCase(rightOperationType))
    		bothOperationType = leftOperationType;
    }
    // или же тип остается "по умолчанию".
    
    bothOperationType = bothOperationType.toLowerCase();    
    String resultVarName = VariableNameManager.getInstance()
        .getUniqueVarNameForOperation("b__Equal");
    
    bldr.append(PrologConstants.PROLOG_EQUAL_OPERATION_NAME).append("(")
        .append(leftVarName).append(",").append(rightVarName).append(",")
        .append(bothOperationType).append(",")
        .append(resultVarName).append(")").append(", ");
    
    // если имеется код, пришедший от левой чайсти операции, то добавим его в
    // результат
    if (sourceResult.getCode() != null)
      bldr.append(sourceResult.getCode());

    // для каждого из аргументов в правой части (а таковой должен быть ровно 1)
    // добавим имеющийся у него код в результат
    for (CodeGeneratorPair argResult : argumentResults) {
      if (argResult.getCode() != null)
        bldr.append(argResult.getCode());
    }

    // Задаем значения полей результата
    result.setCode(bldr.toString());
    result.setResultVarName(resultVarName);
    return result;
  }

  /**
   * Трансляция OCL-операции " and " в код на прологе.
   * <LeftVar> and <RightVar> --> 
   * --> and(<LeftVar>,<RightVar>,<ResultBool>),
   * 
   * @param callExp
   * @param sourceResult
   * @param argumentResults
   * @return
   */
  private CodeGeneratorPair doHandleAndOperation(
      OperationCallExp<Classifier, Operation> callExp,
      CodeGeneratorPair sourceResult, List<CodeGeneratorPair> argumentResults) {
	  
	// переменная, хранящая результат
	CodeGeneratorPair result = new CodeGeneratorPair();

	// билдер, который будет накапливать код
	StringBuilder bldr = new StringBuilder();

	// формируем код операции на прологе 
	// "and(<LeftVar>,<RightVar>,<ResultBool>),".
	String leftVarName = sourceResult.getResultVarName();
	String rightVarName = argumentResults.get(0).getResultVarName();
	String resultVarName = VariableNameManager.getInstance()
	    .getUniqueVarNameForOperation("b__And");
	
	bldr.append(PrologConstants.PROLOG_AND_OPERATION_NAME).append("(")
	    .append(leftVarName).append(",").append(rightVarName).append(",")
	    .append(resultVarName).append(")").append(", ");

	// если имеется код, пришедший от левой чайсти операции, то добавим его в
	// результат
	if (sourceResult.getCode() != null)
	  bldr.append(sourceResult.getCode());

	// для каждого из аргументов в правой части (а таковой должен быть ровно 1)
	// добавим имеющийся у него код в результат
	for (CodeGeneratorPair argResult : argumentResults) {
	  if (argResult.getCode() != null)
	    bldr.append(argResult.getCode());
	}	

	// Задаем значения полей результата
	result.setCode(bldr.toString());
	result.setResultVarName(resultVarName);
	return result;
  }

  /**
   * Трансляция OCL-операции " or " в код на прологе.
   * <LeftVar> or <RightVar> --> 
   * --> or(<LeftVar>,<RightVar>,<ResultBool>),
   * 
   * @param callExp
   * @param sourceResult
   * @param argumentResults
   * @return
   */
  private CodeGeneratorPair doHandleOrOperation(
      OperationCallExp<Classifier, Operation> callExp,
      CodeGeneratorPair sourceResult, List<CodeGeneratorPair> argumentResults) {
    
	  // переменная, хранящая результат
	  CodeGeneratorPair result = new CodeGeneratorPair();

	  // билдер, который будет накапливать код
	  StringBuilder bldr = new StringBuilder();

	  // формируем код операции на прологе 
	  // "or(<LeftVar>,<RightVar>,<ResultBool>),".
	  String leftVarName = sourceResult.getResultVarName();
	  String rightVarName = argumentResults.get(0).getResultVarName();
	  String resultVarName = VariableNameManager.getInstance()
	    .getUniqueVarNameForOperation("b__Or");

	  bldr.append(PrologConstants.PROLOG_OR_OPERATION_NAME).append("(")
	      .append(leftVarName).append(",").append(rightVarName).append(",")
	      .append(resultVarName).append(")").append(", ");
	  
	  // если имеется код, пришедший от левой чайсти операции, то добавим его в
	  // результат
	  if (sourceResult.getCode() != null)
        bldr.append(sourceResult.getCode());

	  // для каждого из аргументов в правой части (а таковой должен быть ровно 1)
	  // добавим имеющийся у него код в результат
	  for (CodeGeneratorPair argResult : argumentResults) {
		if (argResult.getCode() != null)
		  bldr.append(argResult.getCode());
	  }

	  // Задаем значения полей результата
	  result.setCode(bldr.toString());
	  result.setResultVarName(resultVarName);
	  return result;
  }

  /**
   * Трансляция OCL-операции " isEmpty() " в код на прологе.
   * <VarName>->isEmpty() --> 
   * --> isEmpty(<VarName>, <ResultBool>),
   * 
   * @param callExp
   * @param sourceResult
   * @param argumentResults
   * @return
   */
  private CodeGeneratorPair doHandleIsEmptyOperation(
      OperationCallExp<Classifier, Operation> callExp,
      CodeGeneratorPair sourceResult, List<CodeGeneratorPair> argumentResults) {
     
	//  переменная, хранящая результат
    CodeGeneratorPair result = new CodeGeneratorPair();

    // билдер, который будет накапливать код
    StringBuilder bldr = new StringBuilder();

    // формируем код операции на прологе
    // "isEmpty(<VarName>, <ResultBool>),"
    String variableName = sourceResult.getResultVarName();
    String resultBool = VariableNameManager.getInstance()
        .getUniqueVarNameForOperation("b__Empty");
    
    bldr.append(PrologConstants.PROLOG_IS_EMPTY_OPERATION_NAME).append("(")
        .append(variableName).append(",").append(resultBool)
        .append(")").append(", ");

    // Задаем значения полей результата
    result.setCode(bldr.toString());
    result.setResultVarName(resultBool);
    return result;
  }

  /**
   * Трансляция OCL-операции " < " в код на прологе.
   * <LeftVarName> < <RightVarName> --> 
   * --> <(<LeftVarName>, <RightVarName>, <ResultBool>),
   * 
   * @param callExp
   * @param sourceResult
   * @param argumentResults
   * @return
   */
  private CodeGeneratorPair doHandleLessThanOperation(
      OperationCallExp<Classifier, Operation> callExp,
      CodeGeneratorPair sourceResult, List<CodeGeneratorPair> argumentResults) {

	// переменная, хранящая результат
    CodeGeneratorPair result = new CodeGeneratorPair();

    // билдер, который будет накапливать код
    StringBuilder bldr = new StringBuilder();

    // формируем код операции на прологе 
    // "<(<LeftVarName>, <RightVarName>, <ResultBool>),".
    String leftVarName = sourceResult.getResultVarName();
    String rightVarName = argumentResults.get(0).getResultVarName();
    String resultVarName = VariableNameManager.getInstance()
        .getUniqueVarNameForOperation("b__Less");

    bldr.append(PrologConstants.PROLOG_LESS_OPERATION_NAME).append("(")
        .append(leftVarName).append(",").append(rightVarName).append(",")
        .append(resultVarName).append(")").append(", ");

    // если имеется код, пришедший от левой чайсти операции, то добавим его в
    // результат
    if (sourceResult.getCode() != null)
      bldr.append(sourceResult.getCode());

    // для каждого из аргументов в правой части (а таковой должен быть ровно 1)
    // добавим имеющийся у него код в результат
    for (CodeGeneratorPair argResult : argumentResults) {
      if (argResult.getCode() != null)
        bldr.append(argResult.getCode());
    }
    
    // Задаем значения полёй результата
    result.setCode(bldr.toString());
    result.setResultVarName(resultVarName);
    return result;
  }
  
  /**
   * Трансляция OCL-операции " <= " в код на прологе.
   * <LeftVarName> =< <RightVarName> --> 
   * --> =<(<LeftVarName>, <RightVarName>, <ResultBool>),
   * 
   * @param callExp
   * @param sourceResult
   * @param argumentResults
   * @return
   */
  private CodeGeneratorPair doHandleLessThanEqualOperation(
      OperationCallExp<Classifier, Operation> callExp,
      CodeGeneratorPair sourceResult, List<CodeGeneratorPair> argumentResults) {

	// переменная, хранящая результат
    CodeGeneratorPair result = new CodeGeneratorPair();

    // билдер, который будет накапливать код
    StringBuilder bldr = new StringBuilder();

    // формируем код операции на прологе 
    // "=<(<LeftVarName>, <RightVarName>, <ResultBool>),".
    String leftVarName = sourceResult.getResultVarName();
    String rightVarName = argumentResults.get(0).getResultVarName();
    String resultVarName = VariableNameManager.getInstance()
        .getUniqueVarNameForOperation("b__LessEq");

    bldr.append(PrologConstants.PROLOG_LESS_EQUAL_OPERATION_NAME).append("(")
        .append(leftVarName).append(",").append(rightVarName).append(",")
        .append(resultVarName).append(")").append(", ");

    // если имеется код, пришедший от левой чайсти операции, то добавим его в
    // результат
    if (sourceResult.getCode() != null)
      bldr.append(sourceResult.getCode());

    // для каждого из аргументов в правой части (а таковой должен быть ровно 1)
    // добавим имеющийся у него код в результат
    for (CodeGeneratorPair argResult : argumentResults) {
      if (argResult.getCode() != null)
        bldr.append(argResult.getCode());
    }
    
    // Задаем значения полёй результата
    result.setCode(bldr.toString());
    result.setResultVarName(resultVarName);
    return result;
  }

  /**
   * Трансляция OCL-операции " > " в код на прологе.
   * <LeftVarName> > <RightVarName> --> 
   * --> >(<LeftVarName>, <RightVarName>, <ResultBool>),
   * 
   * @param callExp
   * @param sourceResult
   * @param argumentResults
   * @return
   */
  private CodeGeneratorPair doHandleGreaterThanOperation(
      OperationCallExp<Classifier, Operation> callExp,
      CodeGeneratorPair sourceResult, List<CodeGeneratorPair> argumentResults) {
    
	// переменная, хранящая результат
    CodeGeneratorPair result = new CodeGeneratorPair();

    // билдер, который будет накапливать код
    StringBuilder bldr = new StringBuilder();

    // формируем код операции на прологе 
    // ">(<LeftVarName>, <RightVarName>, <ResultBool>),".
    String leftVarName = sourceResult.getResultVarName();
    String rightVarName = argumentResults.get(0).getResultVarName();
    String resultVarName = VariableNameManager.getInstance()
        .getUniqueVarNameForOperation("b__Greater");

    bldr.append(PrologConstants.PROLOG_GREATER_OPERATION_NAME).append("(")
        .append(leftVarName).append(",").append(rightVarName).append(",")
        .append(resultVarName).append(")").append(", ");

    // если имеется код, пришедший от левой чайсти операции, то добавим его в
    // результат
    if (sourceResult.getCode() != null)
      bldr.append(sourceResult.getCode());

    // для каждого из аргументов в правой части (а таковой должен быть ровно 1)
    // добавим имеющийся у него код в результат
    for (CodeGeneratorPair argResult : argumentResults) {
      if (argResult.getCode() != null)
        bldr.append(argResult.getCode());
    }
    
    // Задаем значения полёй результата
    result.setCode(bldr.toString());
    result.setResultVarName(resultVarName);
    return result;
  }
  
  /**
   * Трансляция OCL-операции " >= " в код на прологе.
   * <LeftVarName> >= <RightVarName> --> 
   * --> >=(<LeftVarName>, <RightVarName>, <ResultBool>),
   * 
   * @param callExp
   * @param sourceResult
   * @param argumentResults
   * @return
   */
  private CodeGeneratorPair doHandleGreaterThanEqualOperation(
      OperationCallExp<Classifier, Operation> callExp,
      CodeGeneratorPair sourceResult, List<CodeGeneratorPair> argumentResults) {
    
	// переменная, хранящая результат
    CodeGeneratorPair result = new CodeGeneratorPair();

    // билдер, который будет накапливать код
    StringBuilder bldr = new StringBuilder();

    // формируем код операции на прологе 
    // ">=(<LeftVarName>, <RightVarName>, <ResultBool>),".
    String leftVarName = sourceResult.getResultVarName();
    String rightVarName = argumentResults.get(0).getResultVarName();
    String resultVarName = VariableNameManager.getInstance()
        .getUniqueVarNameForOperation("b__GreaterEq");

    bldr.append(PrologConstants.PROLOG_GREATER_EQUAL_OPERATION_NAME).append("(")
        .append(leftVarName).append(",").append(rightVarName).append(",")
        .append(resultVarName).append(")").append(", ");

    // если имеется код, пришедший от левой чайсти операции, то добавим его в
    // результат
    if (sourceResult.getCode() != null)
      bldr.append(sourceResult.getCode());

    // для каждого из аргументов в правой части (а таковой должен быть ровно 1)
    // добавим имеющийся у него код в результат
    for (CodeGeneratorPair argResult : argumentResults) {
      if (argResult.getCode() != null)
        bldr.append(argResult.getCode());
    }
    
    // Задаем значения полёй результата
    result.setCode(bldr.toString());
    result.setResultVarName(resultVarName);
    return result;
  }
  
  /**
   * Трансляция OCL-операции " oclIsUndefined() " в код на прологе.
   * <VarName>.oclIsUndefined() -->
   * --> isUndefined(<VarName>, <ResultBool>),
   * 
   * @param callExp
   * @param sourceResult
   * @param argumentResults
   * @return
   */
  private CodeGeneratorPair doHandleOclIsUndefinedOperation(
      OperationCallExp<Classifier, Operation> callExp,
      CodeGeneratorPair sourceResult, List<CodeGeneratorPair> argumentResults) {

    // переменная, хранящая результат
    CodeGeneratorPair result = new CodeGeneratorPair();

    // билдер, который будет накапливать код
    StringBuilder bldr = new StringBuilder();

    // формируем код операции на прологе 
    // "isUndefined(<VarName>, <ResultBool>),".
    String varName = sourceResult.getResultVarName();
    String resultVarName = VariableNameManager.getInstance()
        .getUniqueVarNameForOperation("b__OclIsUndefined");

    bldr.append(PrologConstants.PROLOG_OCL_IS_UNDEFINED_OPERATION_NAME)
        .append("(").append(varName).append(",").append(resultVarName)
        .append(")").append(", ");

    // Задаем значения полей результата
    result.setCode(bldr.toString());
    result.setResultVarName(resultVarName);
    return result;
  }
  
  /**
   * Трансляция OCL-операции "size" в код на прологе.
   * {Collection}.size() --> 
   * --> size(<{Collection}>,<ResultInt>),
   * 
   * @param callExp
   * @param sourceResult
   * @param argumentResults
   * @return
   */
  private CodeGeneratorPair doHandleSizeOperation(
      OperationCallExp<Classifier, Operation> callExp,
      CodeGeneratorPair sourceResult, List<CodeGeneratorPair> argumentResults) {
    // переменная, хранящая результат
    CodeGeneratorPair result = new CodeGeneratorPair();

    // билдер, который будет накапливать код
    StringBuilder bldr = new StringBuilder();

    // формируем код операции на прологе "size(<{Collection}>,<ResultInt>),"
    String variableName = sourceResult.getResultVarName();
    String resultInt = VariableNameManager.getInstance()
        .getUniqueVarNameForOperation("i__Size");

    bldr.append(PrologConstants.PROLOG_SIZE_OPERATION_NAME).append("(")
        .append(variableName).append(",").append(resultInt)
        .append(")").append(", ");

    // Задаем значения полёй результата
    result.setCode(bldr.toString());
    result.setResultVarName(resultInt);
    return result;
  }
  
  /**
   * Трансляция OCL-операции "includes(object)" в код на прологе.
   * {Collection}.includes(Object) --> 
   * --> includes(<List>, <Object>, <ResultBool>),
   * 
   * @param callExp
   * @param sourceResult
   * @param argumentResults
   * @return
   */
  private CodeGeneratorPair doHandleIncludesOperation(
      OperationCallExp<Classifier, Operation> callExp,
      CodeGeneratorPair sourceResult, List<CodeGeneratorPair> argumentResults) {
	  
	// переменная, хранящая результат
	CodeGeneratorPair result = new CodeGeneratorPair();

	// билдер, который будет накапливать код
	StringBuilder bldr = new StringBuilder();

	// формируем код операции на прологе 
	// "includes(<List>, <Object>, <ResultBool>),".
	String listVarName = sourceResult.getResultVarName();
	String objectVarName = argumentResults.get(0).getResultVarName();
	String resultBoolVarName = VariableNameManager.getInstance()
	    .getUniqueVarNameForOperation("b__Includes");

	bldr.append(PrologConstants.PROLOG_INCLUDES_OPERATION_NAME).append("(")
	    .append(listVarName).append(",").append(objectVarName).append(",")
	    .append(resultBoolVarName).append(")").append(", ");

	// Задаем значения полей результата
	result.setCode(bldr.toString());
	result.setResultVarName(resultBoolVarName);
	return result;
  }
  
  /**********************************************************************
   * UTILS - METHODS
   *********************************************************************/
  private String createBinaryOperationVariableIteratorCode(
      OperationCallExp<Classifier, Operation> callExp,
      CodeGeneratorPair sourceResult, List<CodeGeneratorPair> argumentResults) {

    StringBuilder resultBuilder = new StringBuilder();

    String sourceVarName = null;
    String argumentVarName = null;

    try {
      sourceVarName = sourceResult.getResultVarName();
      if (sourceVarName != null)
        resultBuilder.append("boolean(").append(sourceVarName).append("), ");
    } catch (NullPointerException e) {
    }

    try {
      CodeGeneratorPair argument = argumentResults.get(0);
      argumentVarName = argument.getResultVarName();
      if (argumentVarName != null)
        resultBuilder.append("boolean(").append(argumentVarName).append("), ");
    } catch (NullPointerException e) {
    }

    switch (callExp.getOperationCode()) {
    case PredefinedType.AND:
      if (sourceVarName != null) {
        resultBuilder.append(sourceVarName).append(",");
      }
      if (argumentVarName != null) {
        resultBuilder.append(argumentVarName).append(",");
      }
      break;

    default:
      break;
    }
    return resultBuilder.toString();
  }

  private String getOperationName(int opCode) {
    switch (opCode) {
    case PredefinedType.SIZE:
      return PredefinedType.SIZE_NAME;
    case PredefinedType.LESS_THAN:
      return PredefinedType.LESS_THAN_NAME;
    case PredefinedType.EQUAL:
      return PredefinedType.EQUAL_NAME;
    case PredefinedType.GREATER_THAN:
      return PredefinedType.GREATER_THAN_NAME;
    case PredefinedType.OCL_IS_UNDEFINED:
      return PredefinedType.OCL_IS_UNDEFINED_NAME;
    case PredefinedType.IS_EMPTY:
      return PredefinedType.IS_EMPTY_NAME;
    case PredefinedType.AND:
      return PredefinedType.AND_NAME;
    case PredefinedType.OR:
      return PredefinedType.OR_NAME;
    case PredefinedType.IMPLIES:
      return PredefinedType.IMPLIES_NAME;
    default:
      return "";
    }
  }
  
  private boolean operandIsOperationResult (String operation) {
	  if ((operation.charAt(0)=='S')||(operation.charAt(0)=='C')
			  ||(operation.charAt(0)=='I')||(operation.charAt(0)=='B')) {
		  return true;
	  }
	  return false;
  }
}
